//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include "gprocess.h"
#include "genumerator.h"
#include <aura.h>

STATIC Int32 g_curEnumId = 0;

STATIC Int32 GetEnumeratorId()
{
    InterlockedCompareExchange((Int32 *)&g_curEnumId, 1000, 0);
    return InterlockedIncrement((Int32 *)&g_curEnumId);
}

CGhostEnumerator::CGhostEnumerator(
    /* [in] */ DLinkNode * pListHead,
    /* [in] */ Int32 * pListState,
    /* [in] */ PVoid pListLock)
{
    assert(NULL != pListHead);
    assert(NULL != pListState);
    assert(NULL != pListLock);

    DLinkNode::Initialize();

    m_pListHead    = pListHead;
    m_pCurrentNode = pListHead;
    m_pListState   = pListState;
    m_hListLock    = pListLock;
    m_originalState = *pListState;

    m_enumeratorId = GetEnumeratorId();
}

UInt32 CGhostEnumerator::AddRef()
{
    Int32 nRefs = m_nRefs.Increment();

    return (UInt32)nRefs;
}

UInt32 CGhostEnumerator::Release()
{
    Int32 nRefs = m_nRefs.Decrement();
    if (0 == nRefs) {
        CGhostEnumeratorFactory factory(m_enumeratorId);
        Ghost::S_DeleteGhost(&factory, this);
        delete this;
    }

    return (UInt32)nRefs;
}

ECode CGhostEnumerator::Current(
    /* [out] */ CGhostProcess ** ppGhost)
{
    CGhostProcess * pGhost = NULL;
    assert(NULL != ppGhost);

    Lock(m_hListLock);
    if ((NULL == m_pCurrentNode)
       || (m_pListHead == m_pCurrentNode)) {
        goto ErrorExit;
    }
    if (*m_pListState != m_originalState) {
        m_pCurrentNode = NULL;
        goto ErrorExit;
    }
    pGhost = THIS(m_pCurrentNode, CGhostProcess, m_inList);
    pGhost->AddRef();
    Unlock(m_hListLock);

    *ppGhost = pGhost;
    return NOERROR;

ErrorExit:
    Unlock(m_hListLock);
    return E_INVALID_OPERATION;
}

ECode CGhostEnumerator::MoveNext(
    /* [out] */ Boolean * pSuccess)
{
    assert(NULL != pSuccess);

    Lock(m_hListLock);
    if (NULL == m_pCurrentNode) {
        goto ErrorExit;
    }
    if (*m_pListState != m_originalState) {
        m_pCurrentNode = NULL;
        goto ErrorExit;
    }

    if (m_pCurrentNode == m_pListHead->Prev()) {
        m_pCurrentNode = NULL;
        *pSuccess = FALSE;
    }
    else {
        m_pCurrentNode = m_pCurrentNode->Next();
        *pSuccess = TRUE;
    }

    Unlock(m_hListLock);
    return NOERROR;

ErrorExit:
    Unlock(m_hListLock);
    return E_INVALID_OPERATION;
}

ECode CGhostEnumerator::Reset()
{
    Lock(m_hListLock);
    m_pCurrentNode = m_pListHead;
    m_originalState = *m_pListState;
    Unlock(m_hListLock);

    return NOERROR;
}

// ----------- static functions -----------------
STATIC DLinkNode s_list;
STATIC Aura_Handle s_hLock = NULL;

ECode CGhostEnumerator::S_Initialize()
{
    s_list.Initialize();
    return (ECode)AuraCriticalSection_Create(&s_hLock);
}

void CGhostEnumerator::S_Destroy()
{
    if (NULL != s_hLock) {
        AuraCriticalSection_Destroy(s_hLock);
        s_hLock = NULL;
    }
}

// ----------------------------------------------
ECode CGhostEnumeratorFactory::CreateGhost(
        /* [out] */ Ghost ** ppGhost,
        /* [out] */ Boolean * pbExisted)
{
    CGhostEnumerator * pGhost = NULL;

    assert(NULL != ppGhost);
    assert(NULL == pbExisted);

    pGhost = new CGhostEnumerator(m_pListHead,
                                 m_pListState,
                                 m_hListLock);
    if (NULL == pGhost) {
        return E_OUT_OF_MEMORY;
    }
    pGhost->AddRef();

    Lock(s_hLock);
    s_list.InsertLast(pGhost);
    Unlock(s_hLock);

    *ppGhost = pGhost;
    return NOERROR;
}

ECode CGhostEnumeratorFactory::GetGhost(
        /* [out] */ Ghost ** ppGhost)
{
    CGhostEnumerator * pGhost = NULL;
    DLinkNode * pHead = &s_list;

    assert(NULL != ppGhost);

    Lock(s_hLock);
    ForEachDLinkNode(CGhostEnumerator*, pGhost, pHead)
    {
        if (pGhost->IsEqual(m_enumeratorId)) {
            pGhost->AddRef();
            Unlock(s_hLock);
            *ppGhost = pGhost;

            return NOERROR;
        }
    }
    Unlock(s_hLock);

    return E_DOES_NOT_EXIST;
}

ECode CGhostEnumeratorFactory::DeleteGhost(
        /* [in] */ Ghost * pGhost)
{
    Lock(s_hLock);
    pGhost->Detach();
    Unlock(s_hLock);

    return NOERROR;
}

